#pragma once
#include<iostream>
#include<string>

#include<cstring>
#include<cerrno>
#include<unistd.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include "log.hpp"

static void service(int sock, const std::string& clientip, const uint16_t& clientport)
{
    //echo server
    char buffer[1024];
    while(true)
    {
        // read && write 可以直接被使用！
        ssize_t s = read(sock, buffer, sizeof(buffer)-1); // 相当于udp recvfrom
        if(s > 0)
        {
            buffer[s] = 0; //将发过来的数据当做字符串
            std::cout << clientip << ":" << clientport << "# " << buffer << std::endl;
        }
        else if(s == 0) //对端关闭连接
        {
            logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);
            break;
        }
        else
        { 
            logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
            break;
        }

        write(sock, buffer, strlen(buffer)); // 先当于 udp sendto
    }
    close(sock); // 这里的sock是serversock，一个客户端对应一个，当不再与这个客户端通信,需要关闭
}


class TcpServer
{
private:
    // listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多
    // 的连接请求就忽略, 这里设置不会太大(一般是5), 
    const static int gbacklog = 30;
public:
    TcpServer(uint16_t port, std::string ip = "0.0.0.0"):_port(port),_listensock(-1)
    {}
    bool initServer()
    {
        // 1. 创建套接字 -- fd
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock < 0)
        {
            logMessage(FATAL, "creat socket error, %d, %s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "create socket success, listensock: %d", _listensock); // 3
        
        // 2. bind -- 文件 + 网络
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
            exit(3);
        }

        // 3. 因为TCP是面向连接的，当正式通信的时候，需要先建立连接
        if (listen(_listensock, gbacklog) < 0)
        {
            logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
            exit(4);
        }

        logMessage(NORMAL, "init server success");
    }
    void start()
    {
         while (true)
        {
            // 4. 获取连接（将连接到的客户端ip和port放在src中）
            struct sockaddr_in src;
            socklen_t len = sizeof(src);
            // servicesock  和 _listensock都是套接字 但是作用不一样
            // _listensock是去获取连接的（像一个招揽工）  servicesock是去提供服务的（像一个服务员）
            // 举例: _listensock不断去招揽客人, 招揽到一个客人到店里面来了，那么就需要一个servicesock服务员去服务客人,每招揽一个，就需要一个servicesock
            // 系统角度: 这里的sock就是一个文件描述符,打印_listensock是3，每次获取到一个客户端来连接服务器,就会分配一个文件描述符给servicesock
            int servicesock = accept(_listensock, (struct sockaddr *)&src, &len);
            if (servicesock < 0)
            {
                logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
                continue;
            }
             // 获取连接成功了
            uint16_t client_port = ntohs(src.sin_port);
            std::string client_ip = inet_ntoa(src.sin_addr);
            logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",servicesock, client_ip.c_str(), client_port);

             // 方式一: 单进程循环 -- 只能够进行一次处理一个客户端，处理完了一个，才能处理下一个
             service(servicesock, client_ip, client_port);
             // tcp 与 udp 中 strat 不一样
             // udp start里面只有一个循环, 不断recvfrom 然后 sendto （所有的客户端都可以给服务器发消息,且都能收到）
             // tcp start里面有两个死循环（A与B,A客户端先发消息, B再发消息，服务器只能收到A发的消息,当A客户端退出，B以前发的
             // 消息一次显示到服务器）故tcp服务器只能服务一个客户端，主要原因是service函数收发消息是一个死循环,只有此客户端退出
             // server函数结束，重新回到开始再与另一个客户端建立通信，拿到对方的ip和port
             // 【可能会想为什么不和udp一样，收发消息不使用死循环】
             // 因为tcp是需要和客户端建立连接的，而udp不需要，udp通信只要拿到服务器ip+port就可以直接给udp服务器发消息 
             // udp是不需要管客户端的存活
             // tcp会给每个客户端分配一个sock来服务与tcp建立连接的客户端，是需要知道客户端的死活的，所以需要一个死循环保持与已建立
             // 连接的客户端保持通信，当客户端关闭连接，tcp服务端会知道，会做出一些处理动作，并会关闭给客户端分配的sock
             // 【所以tcp需要建立多个线程，每一个线程服务一个客户端】
        }
    }       
    ~TcpServer()
    {}
private:
    uint16_t _port;
    std::string _ip;
    int _listensock;
};